class TinyVue {
    constructor(options) {
        this.$options = options;
        this.$data =
            typeof options.data === "function"
                ? options.data.call(this)
                : options.data;

        observe(this.$data);
        proxy(this);
        new Complie(this, options.el);
    }
}

function proxy(vm) {
    Object.keys(vm.$data).forEach((key) => {
        Object.defineProperty(vm, key, {
            get() {
                return vm.$data[key];
            },
            set(newVal) {
                vm.$data[key] = newVal;
            },
        });
    });
}

function observe(obj) {
    if (typeof obj != "object" || obj == null) {
        return obj;
    }
    Object.keys(obj).forEach((key) => {
        defineReactive(obj, key, obj[key]);
    });
}

function defineReactive(obj, key, value) {
    let dep = new Dep();
    Object.defineProperty(obj, key, {
        get() {
            Dep.target && dep.addDep(Dep.target);
            return value;
        },
        set(newVal) {
            if (newVal !== value) {
                value = newVal;
            }
            observe(newVal);
            dep.notify();
        },
    });
}

class Complie {
    constructor(vm, el) {
        this.vm = vm;
        let dom = document.querySelector(el);
        this.complie(dom);
    }
    update(node, exp, dir) {
        const fn = this[dir + "Updator"];
        fn && fn(node, this.vm[exp]);
        new Watcher(this.vm, exp, val => {
            fn && fn(node, val);
        });
    }
    textUpdator(node, val) {
        node.textContent = val;
    }
    complie(dom) {
        let childNodes = dom.childNodes;
        childNodes.forEach((node) => {
            if (this.isElement(node)) {
                console.log("元素节点", node.nodeName);
                if (node.childNodes.length) {
                    this.complie(node);
                }
            } else if (this.isInter(node)) {
                this.complieText(node);
            }
        });
    }
    isElement(node) {
        return node.nodeType === 1;
    }
    isInter(node) {
        return node.nodeType == 3 && /\{\{(.*)\}\}/.test(node.textContent);
    }
    complieText(node) {
        this.update(node, RegExp.$1, "text");
    }
}

class Watcher {
    constructor(vm, key, updator) {
        this.vm = vm;
        this.key = key;
        this.updator = updator;
        Dep.target = this
        this.vm[this.key]
        Dep.target = null
    }
    update() {
        const val = this.vm[this.key]
        this.updator.call(this.vm,val);
    }
}

class Dep {
    constructor() {
        this.subs = [];
    }
    addDep(w) {
        this.subs.push(w);
    }
    notify() {
        this.subs.forEach((w) => w.update());
    }
}
